// Copyright (c) 2024 CHANGLEI. All rights reserved.

import 'dart:math' as math;

import 'package:charts_grasp/src/chart/base/chart_base.dart';
import 'package:charts_grasp/src/chart/chart_series/column_series.dart';
import 'package:charts_grasp/src/chart/chart_series/line_series.dart';
import 'package:charts_grasp/src/chart/chart_series/series.dart';
import 'package:charts_grasp/src/chart/chart_series/series_renderer_properties.dart';
import 'package:charts_grasp/src/chart/chart_series/xy_data_series.dart';
import 'package:charts_grasp/src/chart/common/cartesian_state_properties.dart';
import 'package:charts_grasp/src/chart/series_painter/column_painter.dart';
import 'package:charts_grasp/src/chart/series_painter/line_painter.dart';
import 'package:charts_grasp/src/chart/technical_indicators/accumulation_distribution_indicator.dart';
import 'package:charts_grasp/src/chart/technical_indicators/atr_indicator.dart';
import 'package:charts_grasp/src/chart/technical_indicators/bollinger_bands_indicator.dart';
import 'package:charts_grasp/src/chart/technical_indicators/ema_indicator.dart';
import 'package:charts_grasp/src/chart/technical_indicators/macd_indicator.dart';
import 'package:charts_grasp/src/chart/technical_indicators/momentum_indicator.dart';
import 'package:charts_grasp/src/chart/technical_indicators/rsi_indicator.dart';
import 'package:charts_grasp/src/chart/technical_indicators/sma_indicator.dart';
import 'package:charts_grasp/src/chart/technical_indicators/stochastic_indicator.dart';
import 'package:charts_grasp/src/chart/technical_indicators/tma_indicator.dart';
import 'package:charts_grasp/src/chart/utils/enum.dart';
import 'package:charts_grasp/src/common/event_args.dart';
import 'package:charts_grasp/src/common/utils/enum.dart';
import 'package:charts_grasp/src/common/utils/typedef.dart';
import 'package:flutter/material.dart';

abstract class TechnicalIndicators<T, D> {
  TechnicalIndicators({
    bool? isVisible,
    this.xAxisName,
    this.yAxisName,
    this.seriesName,
    List<double>? dashArray,
    double? animationDuration,
    double? animationDelay,
    this.dataSource,
    ChartValueMapper<T, D>? xValueMapper,
    ChartValueMapper<T, num>? lowValueMapper,
    ChartValueMapper<T, num>? highValueMapper,
    ChartValueMapper<T, num>? openValueMapper,
    ChartValueMapper<T, num>? closeValueMapper,
    this.name,
    this.onRenderDetailsUpdate,
    bool? isVisibleInLegend,
    LegendIconType? legendIconType,
    this.legendItemText,
    Color? signalLineColor,
    double? signalLineWidth,
    int? period,
  })  : isVisible = isVisible ?? true,
        dashArray = dashArray ?? <double>[0, 0],
        animationDuration = animationDuration ?? 1500,
        animationDelay = animationDelay ?? 0,
        isVisibleInLegend = isVisibleInLegend ?? true,
        legendIconType = legendIconType ?? LegendIconType.seriesType,
        signalLineColor = signalLineColor ?? Colors.blue,
        signalLineWidth = signalLineWidth ?? 2,
        period = period ?? 14,
        xValueMapper = (xValueMapper != null) ? ((index) => xValueMapper(dataSource![index], index)) : null,
        lowValueMapper = (lowValueMapper != null) ? ((index) => lowValueMapper(dataSource![index], index)) : null,
        highValueMapper =
            (highValueMapper != null) ? ((index) => highValueMapper(dataSource![index], index)) : null,
        openValueMapper =
            (openValueMapper != null) ? ((index) => openValueMapper(dataSource![index], index)) : null,
        closeValueMapper =
            (closeValueMapper != null) ? ((index) => closeValueMapper(dataSource![index], index)) : null;

  final bool isVisible;

  final String? xAxisName;

  final String? yAxisName;

  final String? seriesName;

  final List<double> dashArray;

  final double animationDuration;

  final double? animationDelay;

  final List<T>? dataSource;

  final ChartIndexedValueMapper<D>? xValueMapper;

  final ChartIndexedValueMapper<num>? lowValueMapper;

  final ChartIndexedValueMapper<num>? highValueMapper;

  final ChartIndexedValueMapper<num>? openValueMapper;

  final ChartIndexedValueMapper<num>? closeValueMapper;

  final String? name;

  final bool isVisibleInLegend;

  final LegendIconType legendIconType;

  final String? legendItemText;

  final Color signalLineColor;

  final double signalLineWidth;

  final int period;

  final ChartIndicatorRenderCallback? onRenderDetailsUpdate;
}

class TechnicalIndicatorsRenderer {
  TechnicalIndicatorsRenderer(this.technicalIndicatorRenderer, this.stateProperties);

  final TechnicalIndicators<dynamic, dynamic> technicalIndicatorRenderer;

  final CartesianStateProperties stateProperties;

  late String name;

  bool? visible;

  bool isIndicator = true;

  final String seriesType = 'indicator';

  late String indicatorType;

  late int index;

  List<CartesianSeriesRenderer> targetSeriesRenderers = <CartesianSeriesRenderer>[];

  List<CartesianChartPoint<dynamic>>? dataPoints = <CartesianChartPoint<dynamic>>[];

  List<CartesianChartPoint<dynamic>> renderPoints = <CartesianChartPoint<dynamic>>[];

  List<CartesianChartPoint<dynamic>>? bollingerUpper = <CartesianChartPoint<dynamic>>[];

  List<CartesianChartPoint<dynamic>>? bollingerLower = <CartesianChartPoint<dynamic>>[];

  List<CartesianChartPoint<dynamic>>? macdLine = <CartesianChartPoint<dynamic>>[];

  List<CartesianChartPoint<dynamic>>? macdHistogram = <CartesianChartPoint<dynamic>>[];

  List<CartesianChartPoint<dynamic>>? stochasticperiod = <CartesianChartPoint<dynamic>>[];

  double? momentumCenterLineValue;

  CartesianChartPoint<dynamic> getDataPoint(dynamic x, num y, CartesianChartPoint<dynamic> sourcePoint, int index,
      [TechnicalIndicators<dynamic, dynamic>? indicator,]) {
    final CartesianChartPoint<dynamic> point = CartesianChartPoint<dynamic>(x, y);
    point.xValue = sourcePoint.xValue;
    point.index = index;
    point.yValue = y;
    point.isVisible = true;
    if (indicator is MacdIndicator &&
        (indicator.macdType == MacdType.histogram || indicator.macdType == MacdType.both)) {
      final MacdIndicator<dynamic, dynamic> macdIndicator = indicator;
      point.pointColorMapper =
          point.yValue >= 0 == true ? macdIndicator.histogramPositiveColor : macdIndicator.histogramNegativeColor;
    }
    return point;
  }

  CartesianChartPoint<dynamic> getRangePoint(
      dynamic x, num high, num low, CartesianChartPoint<dynamic> sourcePoint, int index,
      [TechnicalIndicators<dynamic, dynamic>? indicator,]) {
    final CartesianChartPoint<dynamic> point = CartesianChartPoint<dynamic>(x);
    point.high = high;
    point.low = low;
    point.xValue = sourcePoint.xValue;
    point.index = index;
    point.isVisible = true;
    return point;
  }

  void setSeriesProperties(
    TechnicalIndicators<dynamic, dynamic> indicator,
    String name,
    Color color,
    double width,
    CgCartesianChart chart, [
    bool isLine = false,
    bool isHistogram = false,
  ]) {
    List<double>? dashArray;
    if (indicator.onRenderDetailsUpdate != null && isHistogram == false && isLine == false) {
      TechnicalIndicatorRenderDetails indicators;
      if (indicator is BollingerBandIndicator) {
        final BollingerBandIndicatorRenderParams indicatorRenderParams = BollingerBandIndicatorRenderParams(
            bollingerUpper, bollingerLower, renderPoints, name, width, color, indicator.dashArray,);
        indicators = indicator.onRenderDetailsUpdate!(indicatorRenderParams);
      } else if (indicator is MomentumIndicator) {
        final MomentumIndicatorRenderParams indicatorRenderParams = MomentumIndicatorRenderParams(
            momentumCenterLineValue, renderPoints, name, width, color, indicator.dashArray,);
        indicators = indicator.onRenderDetailsUpdate!(indicatorRenderParams);
      } else if (indicator is StochasticIndicator) {
        final StochasticIndicatorRenderParams indicatorRenderParams =
            StochasticIndicatorRenderParams(stochasticperiod, renderPoints, name, width, color, indicator.dashArray);
        indicators = indicator.onRenderDetailsUpdate!(indicatorRenderParams);
      } else if (indicator is MacdIndicator) {
        final MacdIndicatorRenderParams indicatorRenderParams =
            MacdIndicatorRenderParams(macdLine, macdHistogram, renderPoints, name, width, color, indicator.dashArray);
        indicators = indicator.onRenderDetailsUpdate!(indicatorRenderParams);
      } else {
        final IndicatorRenderParams indicatorRenderParams =
            IndicatorRenderParams(renderPoints, name, width, color, indicator.dashArray);
        indicators = indicator.onRenderDetailsUpdate!(indicatorRenderParams);
      }

      color = indicators.signalLineColor ?? color;
      width = indicators.signalLineWidth ?? width;
      dashArray = indicators.signalLineDashArray ?? indicator.dashArray;
    }
    final CartesianSeries<dynamic, dynamic> series = (isHistogram == true
        ? ColumnSeries<dynamic, dynamic>(
            name: name,
            color: color,
            xAxisName: indicator.xAxisName,
            animationDuration: indicator.animationDuration,
            animationDelay: indicator.animationDelay,
            yAxisName: indicator.yAxisName,
            xValueMapper: (dynamic, _) => null,
            yValueMapper: (dynamic, _) => null,
            dataSource: <dynamic>[],)
        : LineSeries<dynamic, dynamic>(
            name: name,
            color: color,
            dashArray: dashArray ?? indicator.dashArray,
            width: width,
            xAxisName: indicator.xAxisName,
            animationDuration: indicator.animationDuration,
            animationDelay: indicator.animationDelay,
            yAxisName: indicator.yAxisName,
            xValueMapper: (dynamic, _) => null,
            yValueMapper: (dynamic, _) => null,
            dataSource: <dynamic>[],));
    final CartesianSeriesRenderer seriesRenderer =
        (isHistogram == true ? ColumnSeriesRenderer() : LineSeriesRenderer());
    final SeriesRendererDetails seriesRendererDetails = SeriesRendererDetails(seriesRenderer);
    SeriesHelper.setSeriesRendererDetails(seriesRenderer, seriesRendererDetails);
    seriesRendererDetails.stateProperties = stateProperties;
    seriesRendererDetails.series = series;
    seriesRendererDetails.visible = visible;
    seriesRendererDetails.chart = chart;
    seriesRendererDetails.seriesType = (isHistogram ? 'column' : 'line');
    seriesRendererDetails.isIndicator = true;
    seriesRendererDetails.seriesName = name;
    seriesRendererDetails.dashArray = series.dashArray;
    if (seriesRendererDetails.dashArray!.length == 1) {
      seriesRendererDetails.dashArray!.add(series.dashArray[0]);
    }
    targetSeriesRenderers.add(seriesRenderer);
  }

  void setSeriesRange(
      List<CartesianChartPoint<dynamic>> points, TechnicalIndicators<dynamic, dynamic> indicator, List<dynamic> xValues,
      [CartesianSeriesRenderer? seriesRenderer,]) {
    if (seriesRenderer == null) {
      SeriesHelper.getSeriesRendererDetails(targetSeriesRenderers[0]).dataPoints = points;
      SeriesHelper.getSeriesRendererDetails(targetSeriesRenderers[0]).xValues = xValues;
    } else {
      final SeriesRendererDetails seriesRendererDetails = SeriesHelper.getSeriesRendererDetails(seriesRenderer);
      seriesRendererDetails.dataPoints = points;
      seriesRendererDetails.xValues = xValues;
    }
  }

  num getFieldValue(List<CartesianChartPoint<dynamic>?> dataPoints, int index, String valueField) {
    num? val;
    if (valueField == 'low') {
      val = dataPoints[index]?.low;
    } else if (valueField == 'high') {
      val = dataPoints[index]?.high;
    } else if (valueField == 'open') {
      val = dataPoints[index]?.open;
    } else if (valueField == 'y') {
      val = dataPoints[index]?.y;
    } else {
      val = dataPoints[index]?.close;
    }

    val = val ?? 0;
    return val;
  }

  void initDataSource(TechnicalIndicators<dynamic, dynamic> indicator,
      TechnicalIndicatorsRenderer technicalIndicatorsRenderer, CgCartesianChart chart,) {
    technicalIndicatorsRenderer.targetSeriesRenderers = <CartesianSeriesRenderer>[];
    final List<CartesianChartPoint<dynamic>> validData = technicalIndicatorsRenderer.dataPoints!;
    if (validData.isNotEmpty && indicator is AccumulationDistributionIndicator) {
      _calculateADPoints(indicator, validData, technicalIndicatorsRenderer, chart);
    } else if (validData.isNotEmpty &&
        validData.length > indicator.period &&
        indicator is AtrIndicator &&
        indicator.period > 0) {
      _calculateATRPoints(indicator, validData, technicalIndicatorsRenderer, chart);
    } else if (indicator is BollingerBandIndicator && indicator.period > 0) {
      _calculateBollingerBandPoints(indicator, technicalIndicatorsRenderer, chart);
    } else if (validData.isNotEmpty &&
        validData.length > indicator.period &&
        indicator is EmaIndicator &&
        indicator.period > 0) {
      _calculateEMAPoints(indicator, validData, technicalIndicatorsRenderer, chart);
    } else if (indicator is MacdIndicator && indicator.period > 0) {
      _calculateMacdPoints(indicator, technicalIndicatorsRenderer, chart);
    } else if (indicator is MomentumIndicator && indicator.period > 0) {
      _calculateMomentumIndicatorPoints(indicator, technicalIndicatorsRenderer, chart);
    } else if (indicator is RsiIndicator && indicator.period > 0) {
      _calculateRSIPoints(indicator, technicalIndicatorsRenderer, chart);
    } else if (indicator is SmaIndicator && indicator.period > 0) {
      _calculateSMAPoints(indicator, technicalIndicatorsRenderer, chart);
    } else if (indicator is StochasticIndicator && indicator.period > 0) {
      _calculateStochasticIndicatorPoints(indicator, technicalIndicatorsRenderer, chart);
    } else if (validData.isNotEmpty &&
        validData.length > indicator.period &&
        indicator is TmaIndicator &&
        indicator.period > 0) {
      _calculateTMAPoints(indicator, validData, technicalIndicatorsRenderer, chart);
    }
  }

  void _calculateADPoints(
      AccumulationDistributionIndicator<dynamic, dynamic> indicator,
      List<CartesianChartPoint<dynamic>> validData,
      TechnicalIndicatorsRenderer technicalIndicatorsRenderer,
      CgCartesianChart chart,) {
    final List<CartesianChartPoint<dynamic>> points = <CartesianChartPoint<dynamic>>[];
    final List<dynamic> xValues = <dynamic>[];
    CartesianChartPoint<dynamic> point;
    num sum = 0;
    num value = 0;
    num high = 0;
    num low = 0;
    num close = 0;
    for (int i = 0; i < validData.length; i++) {
      high = validData[i].high ?? 0;
      low = validData[i].low ?? 0;
      close = validData[i].close ?? 0;
      value = ((close - low) - (high - close)) / (high - low);
      sum = sum + value * validData[i].volume!;
      point = technicalIndicatorsRenderer.getDataPoint(validData[i].x, sum, validData[i], points.length);
      points.add(point);
      xValues.add(point.x);
    }
    technicalIndicatorsRenderer.renderPoints = points;
    technicalIndicatorsRenderer.setSeriesProperties(
        indicator, indicator.name ?? 'AD', indicator.signalLineColor, indicator.signalLineWidth, chart,);
    technicalIndicatorsRenderer.setSeriesRange(points, indicator, xValues);
  }

  void _calculateATRPoints(
    AtrIndicator<dynamic, dynamic> indicator,
    List<CartesianChartPoint<dynamic>> validData,
    TechnicalIndicatorsRenderer technicalIndicatorsRenderer,
    CgCartesianChart chart,
  ) {
    num average = 0;
    num highLow = 0;
    num highClose = 0;
    num lowClose = 0;
    num trueRange = 0;
    num tempRange = 0;
    final List<CartesianChartPoint<dynamic>> points = <CartesianChartPoint<dynamic>>[];
    final List<dynamic> xValues = <dynamic>[];
    CartesianChartPoint<dynamic> point;
    final List<_TempData> temp = <_TempData>[];
    final num period = indicator.period;
    num sum = 0;
    for (int i = 0; i < validData.length; i++) {
      if (!validData[i].isDrop && !validData[i].isGap) {
        highLow = validData[i].high - validData[i].low;
        if (i > 0) {
          highClose = (validData[i].high - (validData[i - 1].close ?? 0)).abs();
          lowClose = (validData[i].low - (validData[i - 1].close ?? 0)).abs();
        }
        tempRange = math.max(highLow, highClose);
        trueRange = math.max(tempRange, lowClose);
        sum = sum + trueRange;
        if (i >= period && period > 0) {
          average = (temp[temp.length - 1].y * (period - 1) + trueRange) / period;
          point = technicalIndicatorsRenderer.getDataPoint(validData[i].x, average, validData[i], points.length);
          points.add(point);
          xValues.add(point.x);
        } else {
          average = sum / period;
          if (i == period - 1) {
            point = technicalIndicatorsRenderer.getDataPoint(validData[i].x, average, validData[i], points.length);
            points.add(point);
            xValues.add(point.x);
          }
        }
        temp.add(_TempData(validData[i].x, average));
      }
    }
    technicalIndicatorsRenderer.renderPoints = points;
    technicalIndicatorsRenderer.setSeriesProperties(
        indicator, indicator.name ?? 'ATR', indicator.signalLineColor, indicator.signalLineWidth, chart,);
    technicalIndicatorsRenderer.setSeriesRange(points, indicator, xValues);
  }

  void _calculateBollingerBandPoints(
    BollingerBandIndicator<dynamic, dynamic> indicator,
    TechnicalIndicatorsRenderer technicalIndicatorsRenderer,
    CgCartesianChart chart,
  ) {
    final bool enableBand = indicator.bandColor != Colors.transparent;
    final int start = enableBand ? 1 : 0;
    final List<CartesianChartPoint<dynamic>> signalCollection = <CartesianChartPoint<dynamic>>[];
    final List<CartesianChartPoint<dynamic>> upperCollection = <CartesianChartPoint<dynamic>>[];
    final List<CartesianChartPoint<dynamic>> lowerCollection = <CartesianChartPoint<dynamic>>[];
    final List<CartesianChartPoint<dynamic>> bandCollection = <CartesianChartPoint<dynamic>>[];
    final List<dynamic> xValues = <dynamic>[];

    final List<CartesianChartPoint<dynamic>> validData = technicalIndicatorsRenderer.dataPoints!;
    if (validData.isNotEmpty && validData.length >= indicator.period && indicator.period > 0) {
      num sum = 0;
      num deviationSum = 0;
      final num multiplier = indicator.standardDeviation;
      final int limit = validData.length;
      final int length = indicator.period;
      final List<num> smaPoints = List<num>.filled(limit, -1);
      final List<num> deviations = List<num>.filled(limit, -1);
      final List<_BollingerData> bollingerPoints = List<_BollingerData>.filled(
          limit, _BollingerData(x: -1, midBand: -1, lowBand: -1, upBand: -1, visible: false),);

      for (int i = 0; i < length; i++) {
        sum += validData[i].close ?? 0;
      }
      num sma = sum / indicator.period;
      for (int i = 0; i < limit; i++) {
        final num y = validData[i].close ?? 0;
        if (i >= length - 1 && i < limit) {
          if (i - indicator.period >= 0) {
            final num diff = y - validData[i - length].close;
            sum = sum + diff;
            sma = sum / (indicator.period);
            smaPoints[i] = sma;
            deviations[i] = math.pow(y - sma, 2);
            deviationSum += deviations[i] - deviations[i - length];
          } else {
            smaPoints[i] = sma;
            deviations[i] = math.pow(y - sma, 2);
            deviationSum += deviations[i];
          }
          final num range = math.sqrt(deviationSum / (indicator.period));
          final num lowerBand = smaPoints[i] - (multiplier * range);
          final num upperBand = smaPoints[i] + (multiplier * range);
          if (i + 1 == length) {
            for (int j = 0; j < length - 1; j++) {
              bollingerPoints[j] = _BollingerData(
                  x: validData[j].xValue,
                  midBand: smaPoints[i],
                  lowBand: lowerBand.isNaN || lowerBand.isInfinite ? smaPoints[i] : lowerBand,
                  upBand: upperBand.isNaN || upperBand.isInfinite ? smaPoints[i] : upperBand,
                  visible: true,);
            }
          }
          bollingerPoints[i] = _BollingerData(
              x: validData[i].xValue,
              midBand: smaPoints[i],
              lowBand: lowerBand.isNaN || lowerBand.isInfinite ? smaPoints[i] : lowerBand,
              upBand: upperBand.isNaN || upperBand.isInfinite ? smaPoints[i] : upperBand,
              visible: true,);
        } else {
          if (i < indicator.period - 1) {
            smaPoints[i] = sma;
            deviations[i] = math.pow(y - sma, 2);
            deviationSum += deviations[i];
          }
        }
      }
      int i = -1;
      int j = -1;
      for (int k = 0; k < limit; k++) {
        if (k >= (length - 1)) {
          xValues.add(validData[k].x);
          upperCollection.add(technicalIndicatorsRenderer.getDataPoint(
              validData[k].x, bollingerPoints[k].upBand, validData[k], upperCollection.length,),);
          lowerCollection.add(technicalIndicatorsRenderer.getDataPoint(
              validData[k].x, bollingerPoints[k].lowBand, validData[k], lowerCollection.length,),);
          signalCollection.add(technicalIndicatorsRenderer.getDataPoint(
              validData[k].x, bollingerPoints[k].midBand, validData[k], signalCollection.length,),);
          if (enableBand) {
            bandCollection.add(technicalIndicatorsRenderer.getRangePoint(
                validData[k].x, upperCollection[++i].y, lowerCollection[++j].y, validData[k], bandCollection.length,),);
          }
        }
      }
    }
    technicalIndicatorsRenderer.renderPoints = signalCollection;
    technicalIndicatorsRenderer.bollingerUpper = upperCollection;
    technicalIndicatorsRenderer.bollingerLower = lowerCollection;
    bool isLine;
    bool isRangeArea;
    if (indicator.bandColor != Colors.transparent) {
      isLine = false;
      isRangeArea = true;
      technicalIndicatorsRenderer.setSeriesProperties(
          indicator, 'rangearea', indicator.bandColor, 0, chart, isLine, isRangeArea,);
    }
    isLine = true;
    technicalIndicatorsRenderer.setSeriesProperties(
        indicator, indicator.name ?? 'BollingerBand', indicator.signalLineColor, indicator.signalLineWidth, chart,);
    technicalIndicatorsRenderer.setSeriesProperties(
        indicator, 'UpperLine', indicator.upperLineColor, indicator.upperLineWidth, chart, isLine,);
    technicalIndicatorsRenderer.setSeriesProperties(
        indicator, 'LowerLine', indicator.lowerLineColor, indicator.lowerLineWidth, chart, isLine,);
    if (enableBand) {
      technicalIndicatorsRenderer.setSeriesRange(
          bandCollection, indicator, xValues, technicalIndicatorsRenderer.targetSeriesRenderers[0],);
    }
    technicalIndicatorsRenderer.setSeriesRange(
        signalCollection, indicator, xValues, technicalIndicatorsRenderer.targetSeriesRenderers[start],);
    technicalIndicatorsRenderer.setSeriesRange(
        upperCollection, indicator, xValues, technicalIndicatorsRenderer.targetSeriesRenderers[start + 1],);
    technicalIndicatorsRenderer.setSeriesRange(
        lowerCollection, indicator, xValues, technicalIndicatorsRenderer.targetSeriesRenderers[start + 2],);
  }

  void _calculateEMAPoints(EmaIndicator<dynamic, dynamic> indicator, List<CartesianChartPoint<dynamic>> validData,
      TechnicalIndicatorsRenderer technicalIndicatorsRenderer, CgCartesianChart chart,) {
    final int period = indicator.period;
    final List<CartesianChartPoint<dynamic>> points = <CartesianChartPoint<dynamic>>[];
    final List<dynamic> xValues = <dynamic>[];
    CartesianChartPoint<dynamic> point;
    if (validData.length >= period && period > 0) {
      num sum = 0;
      num average = 0;
      final num k = 2 / (period + 1);
      for (int i = 0; i < period; i++) {
        sum += technicalIndicatorsRenderer.getFieldValue(validData, i, indicator.valueField);
      }
      average = sum / period;
      point = technicalIndicatorsRenderer.getDataPoint(
          validData[period - 1].x, average, validData[period - 1], points.length,);
      points.add(point);
      xValues.add(point.x);
      int index = period;
      while (index < validData.length) {
        if (validData[index].isVisible || validData[index].isGap == true || validData[index].isDrop == true) {
          final num prevAverage = points[index - period].y;
          final num yValue =
              (technicalIndicatorsRenderer.getFieldValue(validData, index, indicator.valueField) - prevAverage) * k +
                  prevAverage;
          point = technicalIndicatorsRenderer.getDataPoint(validData[index].x, yValue, validData[index], points.length);
          points.add(point);
          xValues.add(point.x);
        }
        index++;
      }
    }
    technicalIndicatorsRenderer.renderPoints = points;
    technicalIndicatorsRenderer.setSeriesProperties(
        indicator, indicator.name ?? 'EMA', indicator.signalLineColor, indicator.signalLineWidth, chart,);
    technicalIndicatorsRenderer.setSeriesRange(points, indicator, xValues);
  }

  void _calculateMacdPoints(
    MacdIndicator<dynamic, dynamic> indicator,
    TechnicalIndicatorsRenderer technicalIndicatorsRenderer,
    CgCartesianChart chart,
  ) {
    List<CartesianChartPoint<dynamic>> signalCollection = <CartesianChartPoint<dynamic>>[];
    final num fastPeriod = indicator.longPeriod;
    final num slowPeriod = indicator.shortPeriod;
    final num trigger = indicator.period;
    final num length = fastPeriod + trigger;
    List<CartesianChartPoint<dynamic>> macdCollection = <CartesianChartPoint<dynamic>>[];
    List<CartesianChartPoint<dynamic>> histogramCollection = <CartesianChartPoint<dynamic>>[];
    final List<CartesianChartPoint<dynamic>> validData = technicalIndicatorsRenderer.dataPoints!;
    List<dynamic> signalX = <dynamic>[];
    List<dynamic> histogramX = <dynamic>[];
    List<dynamic> macdX = <dynamic>[];
    List<dynamic> collection;
    CartesianSeriesRenderer? histogramSeriesRenderer;
    CartesianSeriesRenderer? macdLineSeriesRenderer;

    if (validData.isNotEmpty &&
        length < validData.length &&
        slowPeriod <= fastPeriod &&
        slowPeriod > 0 &&
        indicator.period > 0 &&
        (length - 2) >= 0) {
      final List<num> shortEMA = _calculateEMAValues(slowPeriod, validData, 'close', technicalIndicatorsRenderer);
      final List<num> longEMA = _calculateEMAValues(fastPeriod, validData, 'close', technicalIndicatorsRenderer);
      final List<num> macdValues = _getMACDVales(indicator, shortEMA, longEMA);
      collection = _getMACDPoints(indicator, macdValues, validData, technicalIndicatorsRenderer);
      macdCollection = collection[0];
      macdX = collection[1];
      final List<num> signalEMA = _calculateEMAValues(trigger, macdCollection, 'y', technicalIndicatorsRenderer);
      collection = _getSignalPoints(indicator, signalEMA, validData, technicalIndicatorsRenderer);
      signalCollection = collection[0];
      signalX = collection[1];
      if (indicator.macdType == MacdType.histogram || indicator.macdType == MacdType.both) {
        collection = _getHistogramPoints(indicator, macdValues, signalEMA, validData, technicalIndicatorsRenderer);
        histogramCollection = collection[0];
        histogramX = collection[1];
      }
    }
    technicalIndicatorsRenderer.renderPoints = signalCollection;
    technicalIndicatorsRenderer.macdHistogram = histogramCollection;
    technicalIndicatorsRenderer.macdLine = macdCollection;
    technicalIndicatorsRenderer.setSeriesProperties(
        indicator, indicator.name ?? 'MACD', indicator.signalLineColor, indicator.signalLineWidth, chart,);
    bool isLine;
    bool isHistogram;
    if (indicator.macdType == MacdType.line || indicator.macdType == MacdType.both) {
      isLine = true;
      technicalIndicatorsRenderer.setSeriesProperties(
          indicator, 'MacdLine', indicator.macdLineColor, indicator.macdLineWidth, chart, isLine,);
    }
    if (indicator.macdType == MacdType.histogram || indicator.macdType == MacdType.both) {
      isLine = false;
      isHistogram = true;
      technicalIndicatorsRenderer.setSeriesProperties(indicator, 'Histogram', indicator.histogramPositiveColor,
          indicator.signalLineWidth / 2, chart, isLine, isHistogram,);
    }
    if (indicator.macdType == MacdType.histogram) {
      histogramSeriesRenderer = technicalIndicatorsRenderer.targetSeriesRenderers[1];
    } else {
      macdLineSeriesRenderer = technicalIndicatorsRenderer.targetSeriesRenderers[1];
      if (indicator.macdType == MacdType.both) {
        histogramSeriesRenderer = technicalIndicatorsRenderer.targetSeriesRenderers[2];
      }
    }
    technicalIndicatorsRenderer.setSeriesRange(
        signalCollection, indicator, signalX, technicalIndicatorsRenderer.targetSeriesRenderers[0],);
    if (histogramSeriesRenderer != null) {
      technicalIndicatorsRenderer.setSeriesRange(histogramCollection, indicator, histogramX, histogramSeriesRenderer);
    }
    if (macdLineSeriesRenderer != null) {
      technicalIndicatorsRenderer.setSeriesRange(macdCollection, indicator, macdX, macdLineSeriesRenderer);
    }
  }

  List<num> _calculateEMAValues(num period, List<CartesianChartPoint<dynamic>> validData, String valueField,
      TechnicalIndicatorsRenderer technicalIndicatorsRenderer,) {
    num sum = 0;
    num initialEMA = 0;
    final List<num> emaValues = <num>[];
    final num emaPercent = 2 / (period + 1);
    for (int i = 0; i < period; i++) {
      sum += technicalIndicatorsRenderer.getFieldValue(validData, i, valueField);
    }
    initialEMA = sum / period;
    emaValues.add(initialEMA);
    num emaAvg = initialEMA;
    for (int j = period.toInt(); j < validData.length; j++) {
      emaAvg = (technicalIndicatorsRenderer.getFieldValue(validData, j, valueField) - emaAvg) * emaPercent + emaAvg;
      emaValues.add(emaAvg);
    }
    return emaValues;
  }

  List<dynamic> _getMACDPoints(MacdIndicator<dynamic, dynamic> indicator, List<num> macdPoints,
      List<CartesianChartPoint<dynamic>> validData, TechnicalIndicatorsRenderer technicalIndicatorsRenderer,) {
    final List<CartesianChartPoint<dynamic>> macdCollection = <CartesianChartPoint<dynamic>>[];
    final List<dynamic> xValues = <dynamic>[];
    int dataMACDIndex = indicator.longPeriod - 1;
    int macdIndex = 0;
    while (dataMACDIndex < validData.length) {
      macdCollection.add(technicalIndicatorsRenderer.getDataPoint(
          validData[dataMACDIndex].x, macdPoints[macdIndex], validData[dataMACDIndex], macdCollection.length,),);
      xValues.add(validData[dataMACDIndex].x);
      dataMACDIndex++;
      macdIndex++;
    }
    return <dynamic>[macdCollection, xValues];
  }

  List<dynamic> _getSignalPoints(MacdIndicator<dynamic, dynamic> indicator, List<num> signalEma,
      List<CartesianChartPoint<dynamic>> validData, TechnicalIndicatorsRenderer technicalIndicatorsRenderer,) {
    int dataSignalIndex = indicator.longPeriod + indicator.period - 2;
    int signalIndex = 0;
    final List<dynamic> xValues = <dynamic>[];
    final List<CartesianChartPoint<dynamic>> signalCollection = <CartesianChartPoint<dynamic>>[];
    while (dataSignalIndex < validData.length) {
      signalCollection.add(technicalIndicatorsRenderer.getDataPoint(
          validData[dataSignalIndex].x, signalEma[signalIndex], validData[dataSignalIndex], signalCollection.length,),);
      xValues.add(validData[dataSignalIndex].x);
      dataSignalIndex++;
      signalIndex++;
    }
    return <dynamic>[signalCollection, xValues];
  }

  List<num> _getMACDVales(MacdIndicator<dynamic, dynamic> indicator, List<num> shortEma, List<num> longEma) {
    final List<num> macdPoints = <num>[];
    final int diff = indicator.longPeriod - indicator.shortPeriod;
    for (int i = 0; i < longEma.length; i++) {
      macdPoints.add(shortEma[i + diff] - longEma[i]);
    }
    return macdPoints;
  }

  List<dynamic> _getHistogramPoints(
      MacdIndicator<dynamic, dynamic> indicator,
      List<num> macdPoints,
      List<num> signalEma,
      List<CartesianChartPoint<dynamic>> validData,
      TechnicalIndicatorsRenderer technicalIndicatorsRenderer,) {
    int dataHistogramIndex = indicator.longPeriod + indicator.period - 2;
    int histogramIndex = 0;
    final List<CartesianChartPoint<dynamic>> histogramCollection = <CartesianChartPoint<dynamic>>[];
    final List<dynamic> xValues = <dynamic>[];
    while (dataHistogramIndex < validData.length) {
      histogramCollection.add(technicalIndicatorsRenderer.getDataPoint(
          validData[dataHistogramIndex].x,
          macdPoints[histogramIndex + (indicator.period - 1)] - signalEma[histogramIndex],
          validData[dataHistogramIndex],
          histogramCollection.length,
          indicator,),);
      xValues.add(validData[dataHistogramIndex].x);
      dataHistogramIndex++;
      histogramIndex++;
    }
    return <dynamic>[histogramCollection, xValues];
  }

  void _calculateMomentumIndicatorPoints(
    MomentumIndicator<dynamic, dynamic> indicator,
    TechnicalIndicatorsRenderer technicalIndicatorsRenderer,
    CgCartesianChart chart,
  ) {
    final List<CartesianChartPoint<dynamic>> signalCollection = <CartesianChartPoint<dynamic>>[];
    final List<CartesianChartPoint<dynamic>> centerLineCollection = <CartesianChartPoint<dynamic>>[];
    final List<CartesianChartPoint<dynamic>> validData = technicalIndicatorsRenderer.dataPoints!;
    final List<dynamic> centerXValues = <dynamic>[];
    final List<dynamic> xValues = <dynamic>[];
    num value;

    if (validData.isNotEmpty) {
      final int length = indicator.period;
      if (validData.length >= indicator.period && indicator.period > 0) {
        for (int i = 0; i < validData.length; i++) {
          centerLineCollection.add(
              technicalIndicatorsRenderer.getDataPoint(validData[i].x, 100, validData[i], centerLineCollection.length),);
          centerXValues.add(validData[i].x);
          if (!(i < length)) {
            value = (validData[i].close ?? 0) / (validData[i - length].close ?? 1) * 100;
            signalCollection.add(
                technicalIndicatorsRenderer.getDataPoint(validData[i].x, value, validData[i], signalCollection.length),);
            xValues.add(validData[i].x);
          }
        }
      }
      technicalIndicatorsRenderer.renderPoints = signalCollection;
      technicalIndicatorsRenderer.momentumCenterLineValue = centerLineCollection.first.y.toDouble();
      const bool isLine = true;
      technicalIndicatorsRenderer.setSeriesProperties(
          indicator, indicator.name ?? 'Momentum', indicator.signalLineColor, indicator.signalLineWidth, chart,);
      technicalIndicatorsRenderer.setSeriesProperties(
          indicator, 'CenterLine', indicator.centerLineColor, indicator.centerLineWidth, chart, isLine,);
      technicalIndicatorsRenderer.setSeriesRange(
          signalCollection, indicator, xValues, technicalIndicatorsRenderer.targetSeriesRenderers[0],);
      technicalIndicatorsRenderer.setSeriesRange(
          centerLineCollection, indicator, centerXValues, technicalIndicatorsRenderer.targetSeriesRenderers[1],);
    }
  }

  void _calculateRSIPoints(
    RsiIndicator<dynamic, dynamic> indicator,
    TechnicalIndicatorsRenderer technicalIndicatorsRenderer,
    CgCartesianChart chart,
  ) {
    final List<CartesianChartPoint<dynamic>> signalCollection = <CartesianChartPoint<dynamic>>[];
    final List<CartesianChartPoint<dynamic>> lowerCollection = <CartesianChartPoint<dynamic>>[];
    final List<CartesianChartPoint<dynamic>> upperCollection = <CartesianChartPoint<dynamic>>[];
    final List<CartesianChartPoint<dynamic>> validData = technicalIndicatorsRenderer.dataPoints!;

    final List<dynamic> xValues = <dynamic>[];
    final List<dynamic> signalXValues = <dynamic>[];

    if (validData.isNotEmpty && validData.length >= indicator.period && indicator.period > 0) {
      if (indicator.showZones) {
        for (int i = 0; i < validData.length; i++) {
          upperCollection.add(technicalIndicatorsRenderer.getDataPoint(
              validData[i].x, indicator.overbought, validData[i], upperCollection.length,),);
          lowerCollection.add(technicalIndicatorsRenderer.getDataPoint(
              validData[i].x, indicator.oversold, validData[i], lowerCollection.length,),);
          xValues.add(validData[i].x);
        }
      }
      num prevClose = validData[0].close ?? 0;
      num gain = 0;
      num loss = 0;
      for (int i = 1; i <= indicator.period; i++) {
        final num close = validData[i].close ?? 0.0;
        if (close > prevClose) {
          gain += close - prevClose;
        } else {
          loss += prevClose - close;
        }
        prevClose = close;
      }
      gain = gain / indicator.period;
      loss = loss / indicator.period;

      signalCollection.add(technicalIndicatorsRenderer.getDataPoint(validData[indicator.period].x,
          100 - (100 / (1 + (gain / loss))), validData[indicator.period], signalCollection.length,),);
      signalXValues.add(validData[indicator.period].x);

      for (int j = indicator.period + 1; j < validData.length; j++) {
        if (!validData[j].isGap && !validData[j].isDrop) {
          final num close = validData[j].close;
          if (close > prevClose) {
            gain = (gain * (indicator.period - 1) + (close - prevClose)) / indicator.period;
            loss = (loss * (indicator.period - 1)) / indicator.period;
          } else if (close < prevClose) {
            loss = (loss * (indicator.period - 1) + (prevClose - close)) / indicator.period;
            gain = (gain * (indicator.period - 1)) / indicator.period;
          }
          prevClose = close;
          signalCollection.add(technicalIndicatorsRenderer.getDataPoint(
              validData[j].x, 100 - (100 / (1 + (gain / loss))), validData[j], signalCollection.length,),);
          signalXValues.add(validData[j].x);
        }
      }
    }
    technicalIndicatorsRenderer.renderPoints = signalCollection;
    const bool isLine = true;
    technicalIndicatorsRenderer.setSeriesProperties(
        indicator, indicator.name ?? 'RSI', indicator.signalLineColor, indicator.signalLineWidth, chart,);
    if (indicator.showZones == true) {
      technicalIndicatorsRenderer.setSeriesProperties(
          indicator, 'UpperLine', indicator.upperLineColor, indicator.upperLineWidth, chart, isLine,);
      technicalIndicatorsRenderer.setSeriesProperties(
          indicator, 'LowerLine', indicator.lowerLineColor, indicator.lowerLineWidth, chart, isLine,);
    }

    technicalIndicatorsRenderer.setSeriesRange(
        signalCollection, indicator, signalXValues, technicalIndicatorsRenderer.targetSeriesRenderers[0],);
    if (indicator.showZones) {
      technicalIndicatorsRenderer.setSeriesRange(
          upperCollection, indicator, xValues, technicalIndicatorsRenderer.targetSeriesRenderers[1],);
      technicalIndicatorsRenderer.setSeriesRange(
          lowerCollection, indicator, xValues, technicalIndicatorsRenderer.targetSeriesRenderers[2],);
    }
  }

  void _calculateSMAPoints(
    SmaIndicator<dynamic, dynamic> indicator,
    TechnicalIndicatorsRenderer technicalIndicatorsRenderer,
    CgCartesianChart chart,
  ) {
    final List<CartesianChartPoint<dynamic>> smaPoints = <CartesianChartPoint<dynamic>>[];
    final List<CartesianChartPoint<dynamic>> points = technicalIndicatorsRenderer.dataPoints!;
    final List<dynamic> xValues = <dynamic>[];
    CartesianChartPoint<dynamic> point;
    if (points.isNotEmpty) {
      final List<CartesianChartPoint<dynamic>> validData = points;

      if (validData.length >= indicator.period && indicator.period > 0) {
        num average = 0;
        num sum = 0;

        for (int i = 0; i < indicator.period; i++) {
          sum += technicalIndicatorsRenderer.getFieldValue(validData, i, indicator.valueField);
        }

        average = sum / indicator.period;
        point = technicalIndicatorsRenderer.getDataPoint(
            validData[indicator.period - 1].x, average, validData[indicator.period - 1], smaPoints.length,);
        smaPoints.add(point);
        xValues.add(point.x);

        int index = indicator.period;
        while (index < validData.length) {
          sum -= technicalIndicatorsRenderer.getFieldValue(validData, index - indicator.period, indicator.valueField);
          sum += technicalIndicatorsRenderer.getFieldValue(validData, index, indicator.valueField);
          average = sum / indicator.period;
          point =
              technicalIndicatorsRenderer.getDataPoint(validData[index].x, average, validData[index], smaPoints.length);
          smaPoints.add(point);
          xValues.add(point.x);
          index++;
        }
      }
      technicalIndicatorsRenderer.renderPoints = smaPoints;
      technicalIndicatorsRenderer.setSeriesProperties(
          indicator, indicator.name ?? 'SMA', indicator.signalLineColor, indicator.signalLineWidth, chart,);
      technicalIndicatorsRenderer.setSeriesRange(smaPoints, indicator, xValues);
    }
  }

  void _calculateStochasticIndicatorPoints(
    StochasticIndicator<dynamic, dynamic> indicator,
    TechnicalIndicatorsRenderer technicalIndicatorsRenderer,
    CgCartesianChart chart,
  ) {
    List<CartesianChartPoint<dynamic>> signalCollection = <CartesianChartPoint<dynamic>>[];
    List<CartesianChartPoint<dynamic>> source = <CartesianChartPoint<dynamic>>[];
    List<CartesianChartPoint<dynamic>> periodCollection = <CartesianChartPoint<dynamic>>[];
    final List<CartesianChartPoint<dynamic>> lowerCollection = <CartesianChartPoint<dynamic>>[];
    final List<CartesianChartPoint<dynamic>> upperCollection = <CartesianChartPoint<dynamic>>[];
    final List<CartesianChartPoint<dynamic>> validData = technicalIndicatorsRenderer.dataPoints!;
    final List<dynamic> xValues = <dynamic>[];
    late List<dynamic> collection;
    late List<dynamic> signalX;
    late List<dynamic> periodX;
    if (validData.isNotEmpty && validData.length >= indicator.period && indicator.period > 0) {
      if (indicator.showZones) {
        for (int i = 0; i < validData.length; i++) {
          upperCollection.add(technicalIndicatorsRenderer.getDataPoint(
              validData[i].x, indicator.overbought, validData[i], upperCollection.length,),);
          lowerCollection.add(technicalIndicatorsRenderer.getDataPoint(
              validData[i].x, indicator.oversold, validData[i], lowerCollection.length,),);
          xValues.add(validData[i].x);
        }
      }
      source = calculatePeriod(indicator.period, indicator.kPeriod.toInt(), validData, technicalIndicatorsRenderer);
      collection =
          _stochasticCalculation(indicator.period, indicator.kPeriod.toInt(), source, technicalIndicatorsRenderer);
      periodCollection = collection[0];
      periodX = collection[1];
      collection = _stochasticCalculation((indicator.period + indicator.kPeriod - 1).toInt(), indicator.dPeriod.toInt(),
          source, technicalIndicatorsRenderer,);
      signalCollection = collection[0];
      signalX = collection[1];
    }
    technicalIndicatorsRenderer.renderPoints = signalCollection;
    technicalIndicatorsRenderer.stochasticperiod = periodCollection;
    const bool isLine = true;
    technicalIndicatorsRenderer.setSeriesProperties(
        indicator, indicator.name ?? 'Stocastic', indicator.signalLineColor, indicator.signalLineWidth, chart,);
    technicalIndicatorsRenderer.setSeriesProperties(
        indicator, 'PeriodLine', indicator.periodLineColor, indicator.periodLineWidth, chart, isLine,);
    if (indicator.showZones) {
      technicalIndicatorsRenderer.setSeriesProperties(
          indicator, 'UpperLine', indicator.upperLineColor, indicator.upperLineWidth, chart, isLine,);
      technicalIndicatorsRenderer.setSeriesProperties(
          indicator, 'LowerLine', indicator.lowerLineColor, indicator.lowerLineWidth, chart, isLine,);
    }
    technicalIndicatorsRenderer.setSeriesRange(
        signalCollection, indicator, signalX, technicalIndicatorsRenderer.targetSeriesRenderers[0],);
    technicalIndicatorsRenderer.setSeriesRange(
        periodCollection, indicator, periodX, technicalIndicatorsRenderer.targetSeriesRenderers[1],);
    if (indicator.showZones) {
      technicalIndicatorsRenderer.setSeriesRange(
          upperCollection, indicator, xValues, technicalIndicatorsRenderer.targetSeriesRenderers[2],);
      technicalIndicatorsRenderer.setSeriesRange(
          lowerCollection, indicator, xValues, technicalIndicatorsRenderer.targetSeriesRenderers[3],);
    }
  }

  List<dynamic> _stochasticCalculation(int period, int kPeriod, List<CartesianChartPoint<dynamic>> data,
      TechnicalIndicatorsRenderer technicalIndicatorsRenderer,) {
    final List<CartesianChartPoint<dynamic>> pointCollection = <CartesianChartPoint<dynamic>>[];
    final List<dynamic> xValues = <dynamic>[];
    if (data.length >= period + kPeriod && kPeriod > 0) {
      final int count = period + (kPeriod - 1);
      final List<num> temp = <num>[];
      final List<num> values = <num>[];
      for (int i = 0; i < data.length; i++) {
        final num value = data[i].y;
        temp.add(value);
      }
      num length = temp.length;
      while (length >= count) {
        num sum = 0;
        for (int i = period - 1; i < (period + kPeriod - 1); i++) {
          sum = sum + temp[i];
        }
        sum = sum / kPeriod;
        final String stochasticSum = sum.toStringAsFixed(2);
        values.add(double.parse(stochasticSum));
        temp.removeRange(0, 1);
        length = temp.length;
      }
      final int len = count - 1;
      for (int i = 0; i < data.length; i++) {
        if (!(i < len)) {
          pointCollection.add(
              technicalIndicatorsRenderer.getDataPoint(data[i].x, values[i - len], data[i], pointCollection.length),);
          xValues.add(data[i].x);
          data[i].y = values[i - len];
        }
      }
    }

    return <dynamic>[pointCollection, xValues];
  }

  List<CartesianChartPoint<dynamic>> calculatePeriod(int period, int kPeriod, List<CartesianChartPoint<dynamic>> data,
      TechnicalIndicatorsRenderer technicalIndicatorsRenderer,) {
    final List<num> lowValue = List<num>.filled(data.length, -1);
    final List<num> highValue = List<num>.filled(data.length, -1);
    final List<num> closeValue = List<num>.filled(data.length, -1);
    final List<CartesianChartPoint<dynamic>> modifiedSource = <CartesianChartPoint<dynamic>>[];

    for (int j = 0; j < data.length; j++) {
      lowValue[j] = data[j].low ?? 0;
      highValue[j] = data[j].high ?? 0;
      closeValue[j] = data[j].close ?? 0;
    }
    if (data.length > period) {
      final List<num> mins = <num>[];
      final List<num> maxs = <num>[];
      for (int i = 0; i < period - 1; ++i) {
        maxs.add(0);
        mins.add(0);
        modifiedSource
            .add(technicalIndicatorsRenderer.getDataPoint(data[i].x, data[i].close, data[i], modifiedSource.length));
      }
      num? min;
      num? max;
      for (int i = period - 1; i < data.length; ++i) {
        for (int j = 0; j < period; ++j) {
          min ??= lowValue[i - j];
          max ??= highValue[i - j];
          min = math.min(min, lowValue[i - j]);
          max = math.max(max, highValue[i - j]);
        }
        maxs.add(max!);
        mins.add(min!);
        min = null;
        max = null;
      }

      for (int i = period - 1; i < data.length; ++i) {
        num top = 0;
        num bottom = 0;
        top += closeValue[i] - mins[i];
        bottom += maxs[i] - mins[i];
        modifiedSource.add(
            technicalIndicatorsRenderer.getDataPoint(data[i].x, (top / bottom) * 100, data[i], modifiedSource.length),);
      }
    }
    return modifiedSource;
  }

  void _calculateTMAPoints(TmaIndicator<dynamic, dynamic> indicator, List<CartesianChartPoint<dynamic>> validData,
      TechnicalIndicatorsRenderer technicalIndicatorsRenderer, CgCartesianChart chart,) {
    final num period = indicator.period;
    final List<CartesianChartPoint<dynamic>> points = <CartesianChartPoint<dynamic>>[];
    final List<dynamic> xValues = <dynamic>[];
    CartesianChartPoint<dynamic> point;
    if (validData.isNotEmpty && validData.length >= indicator.period && period > 0) {
      if (validData.isNotEmpty && validData.length >= period) {
        num sum = 0;
        int index = 0;
        List<num> smaValues = <num>[];
        int length = validData.length;

        while (length >= period) {
          sum = 0;
          index = validData.length - length;
          for (int j = index; j < index + period; j++) {
            sum += technicalIndicatorsRenderer.getFieldValue(validData, j, indicator.valueField);
          }
          sum = sum / period;
          smaValues.add(sum);
          length--;
        }
        for (int k = 0; k < period - 1; k++) {
          sum = 0;
          for (int j = 0; j < k + 1; j++) {
            sum += technicalIndicatorsRenderer.getFieldValue(validData, j, indicator.valueField);
          }
          sum = sum / (k + 1);
          smaValues = _splice(smaValues, k, sum);
        }

        index = indicator.period;
        while (index <= smaValues.length) {
          sum = 0;
          for (int j = index - indicator.period; j < index; j++) {
            sum = sum + smaValues[j];
          }
          sum = sum / indicator.period;
          point = technicalIndicatorsRenderer.getDataPoint(
              validData[index - 1].x, sum, validData[index - 1], points.length,);
          points.add(point);
          xValues.add(point.x);
          index++;
        }
      }
    }
    technicalIndicatorsRenderer.renderPoints = points;
    technicalIndicatorsRenderer.setSeriesProperties(
        indicator, indicator.name ?? 'TMA', indicator.signalLineColor, indicator.signalLineWidth, chart,);
    technicalIndicatorsRenderer.setSeriesRange(points, indicator, xValues);
  }

  List<num> _splice<num>(List<num> list, int index, num? elements) {
    if (elements != null) {
      list.insertAll(index, <num>[elements]);
    }
    return list;
  }
}

class _TempData {
  _TempData(this.x, this.y);

  final dynamic x;
  final num y;
}

class _BollingerData {
  _BollingerData({this.x, required this.midBand, required this.lowBand, required this.upBand, required this.visible});

  num? x;
  num midBand;
  num lowBand;
  num upBand;
  bool visible;
}
